#ifndef SOFTMEM_H
#define SOFTMEM_H

#include "node.h"
#include "lane.h"
#include "port.h"
#include "channel.h"
#include <complex>

#include "ip/fabrics.h"

#include "ip/alu.h"

#include "ip/erunner.h"

#include "ip/opdef.h"

typedef struct PackElement
{
    bool sb;
    bool msgin;
    bool ain;
    std::complex<float> data;
} PackElement;

typedef struct LaneChannelShadowBufferFabricBusPack
{
    int dst_uri;
    std::complex<float> r_data;
    int sb_addr;
    int ain_addr;
} LaneChannelShadowBufferFabricBusPack;

class LaneChannelShadowBufferFabricBusReceiver : public LanePortReceiver
{
public:
    PortIn<int> * dst_uri;
    PortIn<std::complex<float>> * r_data;
    PortIn<int> * sb_addr;
    PortIn<int> * ain_addr;

    LaneChannelShadowBufferFabricBusReceiver(JObject *parent = nullptr);
    LaneChannelShadowBufferFabricBusPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelShadowBufferFabricBusSender : public LanePortSender
{
public:
    PortOut<int> * dst_uri;
    PortOut<std::complex<float>> * r_data;
    PortOut<int> * sb_addr;
    PortOut<int> * ain_addr;

    LaneChannelShadowBufferFabricBusSender(JObject *parent = nullptr);
    void connect(LaneChannelShadowBufferFabricBusReceiver *sender);
    void put(int data_dst_uri, std::complex<float> data_r_data, int data_sb_addr, int data_ain_addr);
    void put_bypass(LaneChannelShadowBufferFabricBusReceiver *receiver);
    bool is_any_full();
};

class ChannelShadowBufferFabricReceiver : public ChannelPortReceiver
{
public:
    LaneChannelShadowBufferFabricBusReceiver * Bus;

    ChannelShadowBufferFabricReceiver(JObject *parent);
};

class ChannelShadowBufferFabricSender : public ChannelPortSender
{
public:
    LaneChannelShadowBufferFabricBusSender * Bus;

    ChannelShadowBufferFabricSender(JObject *parent);
};

class ChannelShadowBufferFabric : public Channel
{
public:
    static void connect(ChannelShadowBufferFabricSender *sender, ChannelShadowBufferFabricReceiver *receiver);
};

typedef struct NodeSoftmemParam
{
    int softmem_size;
    int num_of_main_memory;
    int num_of_alu;
    int multiplexer;
} NodeSoftmemParam;

class NodeSoftmem : public Node
{
public:
    int const_num_of_alu;
    int const_multiplexer;
    List<PackElement> reg_data;
    List<int> reg_alu_dst_pid;
    List<bool> reg_alu_sb;
    List<int> reg_alu_dst_uri;
    List<int> reg_alu_addr0;
    List<bool> reg_alu_addr0_wait;
    List<bool> reg_alu_addr0_ain;
    List<int> reg_alu_addr1;
    List<bool> reg_alu_addr1_wait;
    List<bool> reg_alu_addr1_ain;
    List<int> reg_alu_op_code;
    List<bool> reg_alu_has_work;
    List<bool> reg_alu_working;
    int reg_w_ain_addr;
    int reg_w_sb_addr;
    int reg_w_addr;
    std::complex<float> reg_w_data;
    bool reg_w_clr;
    int reg_sb_ain_addr;
    int reg_sb_sb_addr;
    int reg_sb_w_addr;
    std::complex<float> reg_sb_w_data;
    int reg_m_ain_addr;
    int reg_m_sb_addr;
    int reg_m_addr;
    std::complex<float> reg_m_data;
    int reg_alu_dst_addr;
    std::complex<float> reg_alu_dst_data;
    List<ChannelLoadFabricReceiver *> port_memory_receivers;
    List<ChannelStoreFabricSender *> port_memory_senders;
    ChannelLoadFabricReceiver * port_mrunner_receiver;
    ChannelLoadFabricSender * port_mrunner_sender;
    List<ChannelAluSender *> port_alu_senders;
    List<ChannelERunnerSoftmemReceiver *> port_erunner_receivers;
    ChannelShadowBufferFabricReceiver * port_shadow_buffer;
    ChannelShadowBufferFabricSender * port_sb_save;
    List<PortOut<bool> *> port_halt_alu;

    NodeSoftmem(Node *parent, NodeSoftmemParam params);
    NodeTaskList tasks() const override;

    void task_main_memory_load();
    void task_shadow_buffer_load();
    void task_mrunner_load();
    void task_erunner_load();
    void task_alu_load();
    void task_deploy_request();
};

#endif // SOFTMEM_H